package com.prpat;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.regex.MatchResult;

public class StateTrans 
{
	private static LinkedList<String> hashes = new LinkedList<String>();
	public static LinkedList<Range> ranges = new LinkedList<Range>();
	private static LinkedList<Range> currRanges = null;
	private static Statholder current = null;
	public static Thread server;
	
	public static void initialState()
	{
		System.out.println("Loading levels and hashes...");
		readLevels();
		readHashes();
		System.out.println("Levels and hashes loaded!");
	}

	public static synchronized void newHash()
	{
		if(!hashes.isEmpty())
		{
			current = new Statholder(hashes.poll());
			currRanges = new LinkedList<Range>(ranges);
			PendingContainer.create();
			
			server = new Thread(new Server());
			server.start();
		}
		else{
			River.suicider();//When we finished all hashes - Quit
		}
	}
	
	public static void sealHash(String mes, String result, String source) 
	{
		try 
		{
			FileWriter fstream = new FileWriter("utils/log.log", true);
			BufferedWriter out = new BufferedWriter(fstream);
			if(result.equalsIgnoreCase("Uncracked"))
				out.write(current.closer(current.getHash(), result, source));
			else
				out.write(current.closer(mes, result, source));
			//Close the output stream
			out.close();
		} 
		catch (IOException e) {
			System.err.println(e.getMessage());
		}
		System.out.println("----------------- Stopping current hash server -------------------");
		server.interrupt();
		newHash();
	}

	private static void readLevels()
	{
		try
		{
			// Open the file that is the first command line parameter
			FileInputStream fstream = new FileInputStream("utils/pass.lvl");
			// Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			//Read File Line By Line
			while ((strLine = br.readLine()) != null)   
				analyzeLine(strLine);
			
			//Close the input stream
			in.close();
		}
		catch (Exception e)
		{
			System.err.println("StateTrans, readLevels, " + e.getMessage());
		}
	}

	private static void readHashes()
	{
		try
		{
			// Open the file that is the first command line parameter
			FileInputStream fstream = new FileInputStream("utils/pass.md5");
			// Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			//Read File Line By Line
			while ((strLine = br.readLine()) != null)   
				hashes.add(strLine);
			//Close the input stream
			in.close();
		}
		catch (Exception e)
		{
			System.err.println("Globals, readHashes, " + e.getMessage());
		}	
	}

	private static void analyzeLine(String strLine)
	{
		long treshhold = 949809509;
		Scanner s1 = new Scanner(strLine);

		s1.findInLine("lvl=(\\d+) \\((\\d+) KB for nbparts\\) (\\d+) [KMG] possible passwords \\((\\d+)\\)");

		MatchResult result1 = s1.match();
		int lvl = Integer.parseInt(result1.group(1));
		long end = Long.parseLong(result1.group(4));
		long itr = end/treshhold;
		if(itr > 0)
		{
			int i = 0;
			for( ; i < itr ; i++)
			{
				ranges.add(new Range(lvl, i*treshhold, ((i+1)*treshhold)));
			}
			ranges.add(new Range(lvl, i*treshhold, 0));
		}
		else
			ranges.add(new Range(lvl, 0, 0));

		s1.close();
	}

	public static synchronized void enqRange(Range range) 
	{
		currRanges.add(range);
	}

	public static synchronized boolean isCurrRangesEmpty() 
	{
		if (!currRanges.isEmpty()){
			return false;
		}
		else{
			if(PendingContainer.isEmpty()){
				return true;
			}
			else{
				currRanges = PendingContainer.toList();
				PendingContainer.create();
				return false;
			}	
		}
	}

	public static synchronized Message deqRng() {
		synchronized (StateTrans.class) {
			if (!currRanges.isEmpty()) {
				return new Message(current.getHash(), currRanges.poll(),
						Message.REQUEST);
			}
		}
		return  null;
	}

	public static boolean isRelevant(Message toSend) {
		if(toSend.getType()==Message.REPLY){
			return true;
		}
		boolean ans = false;
		synchronized (StateTrans.class) {
			ans = current!=null && current.hash.equalsIgnoreCase(toSend.getMessage()); 
		}
		return ans;
	}
}
